﻿using ICSharpCode.SharpZipLib.Zip;
using System;
using System.IO;

namespace Util.Compress
{
    /// <summary>
    /// ICSharpCode.SharpZipLib.Zip 工具类
    /// </summary>
    public class SharpZipLibUtils
    {
        /// <summary>
        /// 压缩
        /// </summary>
        /// <param name="filePath">待压缩的文件路径 或 文件夹路径</param>
        /// <param name="zipFilePath">压缩后的文件路径</param>
        /// <param name="level">压缩等级, 默认值 9, 0 - store only to 9 - means best compression</param>
        /// <param name="password">密码</param>
        public static void Zip(string filePath, string zipFilePath, int level = 9, string password = null)
        {
            using (ZipOutputStream zipOutputStream = new ZipOutputStream(File.Create(zipFilePath)))
            {
                zipOutputStream.SetLevel(level);
                if (password != null)
                {
                    zipOutputStream.Password = password;
                }

                string[] filePaths;
                bool isDir = false;
                DirectoryInfo di = new DirectoryInfo(filePath);
                if (di.Exists) // filePath 是文件还是文件夹
                {
                    filePaths = di.GetFilePathsAdv(); // 是文件夹, 获取文件夹旗下所有文件
                    isDir = true;
                }
                else
                {
                    filePaths = new string[1] { filePath }; // 是文件
                }

                byte[] buffer = new byte[4096];

                foreach (string item in filePaths)
                {
                    ZipEntry toAdd_ZipEntry;
                    if (isDir)
                    {
                        toAdd_ZipEntry = new ZipEntry(Util.IO.DirectoryUtils.GetRelativePath(item, filePath));
                    }
                    else
                    {
                        toAdd_ZipEntry = new ZipEntry(Path.GetFileName(item));
                    }

                    toAdd_ZipEntry.DateTime = File.GetCreationTime(item);
                    zipOutputStream.PutNextEntry(toAdd_ZipEntry);

                    using (FileStream fs = new FileStream(item, FileMode.Open, FileAccess.Read))
                    {
                        int sourceBytes;
                        do
                        {
                            sourceBytes = fs.Read(buffer, 0, buffer.Length);
                            zipOutputStream.Write(buffer, 0, sourceBytes);
                        }
                        while (sourceBytes > 0);
                    }
                }

                zipOutputStream.Finish();
                zipOutputStream.Close();
            }
        }

        // TODO 目前遇到的坑, 在 winrar 加密需要选中 ZIP 使用传桶加密法, 才能被解密
        /// <summary>
        /// 解压
        /// </summary>
        /// <param name="zipFilePath"></param>
        /// <param name="unzipDirectoryPath"></param>
        /// <param name="password"></param>
        public static void Unzip(string zipFilePath, string unzipDirectoryPath, string password = null)
        {
            using (ZipInputStream zipStream = new ZipInputStream(File.Open(zipFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)))
            {
                if (password != null)
                {
                    zipStream.Password = password;
                }

                ZipEntry zipEntry;

                while ((zipEntry = zipStream.GetNextEntry()) != null)
                {
                    string filePath = System.IO.Path.Combine(unzipDirectoryPath, $"{zipEntry.Name}");
                    FileInfo fi = new FileInfo(filePath);
                    if (fi.Directory.Exists == false) fi.Directory.Create();
                    // TODO 文件已存在 ? 添加参数 覆盖 or 报错
                    using (FileStream fileStreamOut = new FileStream(filePath, FileMode.Create, FileAccess.Write))
                    {
                        int size = 2048;
                        byte[] buffer = new byte[size];
                        while (true)
                        {
                            size = zipStream.Read(buffer, 0, buffer.Length);
                            if (size > 0)
                            {
                                fileStreamOut.Write(buffer, 0, size);
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                }
            }
        }
    }
}
